
#include "UtilLoader.h"
#undef GetEnvironmentVariable

using namespace ttg_internal::utils;
using namespace System::IO;

//------------------
//--- UtilLoader ---
//------------------

static UtilLoader::UtilLoader()
{
	//Setting resolver.
	System::AppDomain::CurrentDomain->AssemblyResolve +=
		gcnew System::ResolveEventHandler(UtilLoader::MyResolveEventHandler);
}

IEnumerable<System::String ^> ^UtilLoader::GetUtilityDirs()
{
	List<System::String ^> ^res = gcnew List<System::String ^>();
	res->Add(".");
	res->Add("./Utils");
	System::String ^home = System::Environment::GetEnvironmentVariable("TTGLIB_HOME");
	if (home != nullptr)
		res->Add(home + "/Bin/Utils");
	return res;
}

IEnumerable<System::String ^> ^UtilLoader::GetUtilManagerPathes()
{
	List<System::String ^> ^res = gcnew List<System::String ^>();
	res->Add("../UtilManager.dll");
	System::String ^home = System::Environment::GetEnvironmentVariable("TTGLIB_HOME");
	if (home != nullptr)
		res->Add(home + "/Bin/UtilManager.dll");
	res->Add("./UtilManager.dll");
	return res;
}

Assembly^ UtilLoader::MyResolveEventHandler(System::Object^ sender, System::ResolveEventArgs^ args)
{
	if (args->Name->Contains("UtilManager"))
	{
		for each (System::String ^path in GetUtilManagerPathes())
		{
			try
			{ return Assembly::LoadFrom(path); }
			catch (System::Exception ^) { }
		}
	}
	return nullptr;
}

IUtility ^UtilLoader::LoadUtility(System::String ^name, System::String ^&error)
{
	error = "All ok";
	IEnumerable<System::String ^> ^dirs = GetUtilityDirs();
	for each (System::String ^path in dirs)
	{
		try
		{
			array<System::String ^> ^assemblies = Directory::GetFiles(path, "*.dll");
			for each (System::String ^file in assemblies)
			{
				try
				{
					Assembly ^ass = Assembly::LoadFrom(file);
					array<System::Type ^> ^types = ass->GetTypes();
					for each (System::Type ^t in types)
						if (t->Name == name &&
							IUtility::typeid->IsAssignableFrom(t))
						{
							return safe_cast<IUtility ^>(System::Activator::CreateInstance(t));
						}
				} catch (System::Exception ^) { }
			}
		}
		catch (System::Exception ^) { }
	}
	error = System::String::Format("Cannot find utility \"{0}\". Some functionality will be disabled.\n"\
								   "You can try to (re)install the latest version of ttgLib.", name);
	return nullptr;
}

IUtility ^UtilLoader::LoadUtilityFromFile(System::String ^filename, System::String ^&error)
{
	error = "All ok";
	try
	{
		Assembly ^ass = Assembly::LoadFrom(filename);
		array<System::Type ^> ^types = ass->GetTypes();
		for each (System::Type ^t in types)
			if (IUtility::typeid->IsAssignableFrom(t))
			{
				return safe_cast<IUtility ^>(System::Activator::CreateInstance(t));
			}
		return nullptr;
	}
	catch (System::Exception ^ex)
	{
		error = ex->Message;
		return nullptr;
	}
}


List<System::String ^> ^UtilLoader::findAvailableUtilities()
{
	IEnumerable<System::String ^> ^dirs = GetUtilityDirs();
	List<System::String ^> ^res = gcnew List<System::String ^>();
	for each (System::String ^path in dirs)
	{
		try
		{
			array<System::String ^> ^assemblies = Directory::GetFiles(path, "*.dll");
			for each (System::String ^file in assemblies)
			{
				try
				{
					Assembly ^ass = Assembly::LoadFrom(file);
					array<System::Type ^> ^types = ass->GetTypes();
					for each (System::Type ^t in types)
						if (IUtility::typeid->IsAssignableFrom(t) &&
							t->Name != "Utility" &&
							t->Name != "IUtility" &&
							res->IndexOf(t->Name) < 0)
							res->Add(t->Name);
				} catch (System::Exception ^) { }
			}
		}
		catch (System::Exception ^) { }
	}
	return res;
}
